library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ---------------------------------------------------------------------------------------------------
as.difftime(): lubridate, base
date():        lubridate, base
filter():      dplyr, stats
intersect():   lubridate, base
lag():         dplyr, stats
setdiff():     lubridate, base
union():       lubridate, base
library(stringr)
library(anytime)
library(lubridate)
library(skimr)
library(listviewer)
library(rlang)

Attaching package: ‘rlang’

The following objects are masked from ‘package:purrr’:

    %||%, %@%, as_function, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl, invoke, is_atomic,
    is_bare_atomic, is_bare_character, is_bare_double, is_bare_integer, is_bare_list, is_bare_logical,
    is_bare_numeric, is_bare_vector, is_character, is_double, is_empty, is_formula, is_function, is_integer,
    is_list, is_logical, is_null, is_scalar_atomic, is_scalar_character, is_scalar_double, is_scalar_integer,
    is_scalar_list, is_scalar_logical, is_scalar_vector, is_vector, list_along, prepend, rep_along, set_names,
    splice

The following object is masked from ‘package:tibble’:

    has_name
library(forcats)
library(ggjoy)
library(padr)
library(hrbrthemes)
hrbrthemes is under *active* development. See https://github.com/hrbrmstr/hrbrthemes for info/news.
library(janitor)
load("output/trennid_toodeldud.RData")

Tutvu andmetega

glimpse(trennid_toodeldud)
Observations: 75,808
Variables: 14
$ profile          <dbl> 10005837, 10005837, 10005837, 10005837, 10005837, 10005837, 10005837, 10005837, 10005837, 10005837...
$ synnipaev        <date> 1978-06-26, 1978-06-26, 1978-06-26, 1978-06-26, 1978-06-26, 1978-06-26, 1978-06-26, 1978-06-26, 1...
$ sugu             <chr> "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "M...
$ pikkus_cm        <dbl> 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 180, 180, 169, 169, 169, 169, 169...
$ kaal_kg          <dbl> 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, NA, NA, NA, NA, NA, NA, NA, NA, N...
$ lemmik_spordiala <chr> "Cycling (Sport)", "Cycling (Sport)", "Cycling (Sport)", "Cycling (Sport)", "Cycling (Sport)", "Cy...
$ workout          <dbl> 519605661, 349744705, 345915031, 345404462, 344247597, 341731612, 340579183, 340106467, 339578378,...
$ workout_date     <dttm> 2017-05-10 12:35:00, 2017-06-01 14:41:00, 2017-05-25 14:42:00, 2017-05-24 16:10:00, 2017-05-22 16...
$ spordiala        <chr> "riding", "cycling", "cycling", "cycling", "cycling", "cycling", "doing weight training", "cycling...
$ trenni_kestvus   <S4: Period> 59S, 1H 10M 22S, 1H 12M 16S, 59M 46S, 1H 13M 22S, 1H 7M 0S, 1H 29M 22S, 1H 9M 54S, 1H 14M 0...
$ distants_km      <dbl> NA, 27.20, 27.36, 21.29, 30.07, 26.20, NA, 26.58, 27.16, 23.22, 6.55, 1.06, 2.84, NA, NA, NA, 4.01...
$ raw_profile_info <chr> "\n                Country:\n                        Estonia\n                    Birthday:\n     ...
$ raw_workout_date <chr> "May 10 at 11:35", "June 01 at 13:41", "May 25 at 13:42", "May 24 at 15:10", "May 22 at 15:58", "M...
$ raw_workout_info <chr> "was out riding for 0m:59s.", "was out cycling. He tracked 27.20 km in 1h:10m:22s.", "was out cycl...

Ülevaade kõigist veergudest

# Funktsioon leiab valitud veeru kõik unikaalsed väärtused koos esinemissagedusega
unikaalsed <- function(x){
    valik <- sym(x)
    
    p <- trennid_toodeldud %>% 
        mutate(uus = str_replace_na(!!valik)) %>% 
        count(uus) %>% 
        mutate(uus = str_c(uus, " (", n, ")"),
               uus = fct_reorder(uus, n)) %>% 
        arrange(desc(n)) %>% 
        select(uus)
    
    colnames(p) <- x
    
    return(p)
}
# kõik veerud, mis pole ID või muul põhjusel välistatud
veerud <- trennid_toodeldud %>% 
    select(-profile, -workout, -contains("raw")) %>%
    colnames()
# leia data frame kõigi veergude unikaalsed väärtused
p <- map(veerud, unikaalsed)
# kuva unikaalsed väärtused
p %>% 
    map(., as.list) %>% 
    flatten() %>% 
    jsonedit()

TOP 15 populaarsemat spordiala

top_15_spordiala <- trennid_toodeldud %>% 
  filter(spordiala != "exercising") %>%  # välista mittemidagiütlev "exercising"
  count(spordiala, sort = TRUE) %>% 
  head(15) %>% 
  .$spordiala
top_15_spordiala
 [1] "walking"                "running"                "cycling"                "mountain biking"       
 [5] "skating"                "doing weight training"  "skiing"                 "hiking"                
 [9] "doing aerobics"         "doing circuit training" "swimming"               "orienteering"          
[13] "roller skiing"          "golfing"                "dancing"               

TOP 10 lemmik spordiala

top_10_lemmik_spordiala <- trennid_toodeldud %>% 
  distinct(profile, lemmik_spordiala) %>% 
  mutate(lemmik_spordiala = case_when(.$lemmik_spordiala == "Walking (Fitness)" ~ "walking",
                                      .$lemmik_spordiala == "Skiing (Cross country)" ~ "skiing",
                                      .$lemmik_spordiala == "Football (Soccer)" ~ "soccer",
                                      TRUE ~ .$lemmik_spordiala),
         lemmik_spordiala = str_to_lower(lemmik_spordiala)) %>% 
  count(lemmik_spordiala, sort = TRUE) %>% 
  head(10) %>% 
  .$lemmik_spordiala
top_10_lemmik_spordiala
 [1] "walking"             "running"             "cycling (sport)"     "cycling (transport)" "mountain biking"    
 [6] "roller skating"      "weight training"     "skiing"              "orienteering"        "soccer"             

Populaarsemad spordialad eesti keeles

spordiala_est <- tribble(
  ~spordiala, ~spordiala_est,
  "walking", "käimine",
  "running", "jooksmine",
  "cycling", "jalgrattasõit",
  "cycling (sport)", "jalgrattasõit (sport)",
  "cycling (transport)", "jalgrattasõit (transport)",
  "mountain biking", "maastikuratta sõit",
  "skating", "rulluisutamine",
  "roller skating", "rulluisutamine",
  "doing weight training", "jõutrenn",  
  "weight training", "jõutrenn",  
  "skiing", "suusatamine",  
  "hiking", "matkamine",  
  "dancing", "tantsimine",  
  "doing aerobics", "aeroobika",
  "doing circuit training", "ringtreening",
  "swimming", "ujumine",  
  "orienteering", "orienteerumine",  
  "roller skiing", "rullsuusatamine",  
  "golfing", "golf",
  "soccer", "jalgpall"
)

Ülevaade analüüsis kasutatud trennide ja inimetse arvust

trennid_toodeldud %>% 
  filter(!is.na(spordiala)) %>% 
  group_by(spordiala) %>% 
  summarise(trennide_arv = n(),
            kasutajate_arv = n_distinct(profile)) %>% 
  ungroup() %>% 
  mutate(label = str_c(trennide_arv, " (", kasutajate_arv, ")")) %>% 
  arrange(desc(trennide_arv)) %>% 
  head(30) %>% 
  ggplot(aes(fct_reorder(spordiala, trennide_arv), trennide_arv)) +
  geom_col() +
  geom_text(aes(label = label), hjust = -0.1) +
  scale_y_continuous(breaks = (seq(0, 35000, by = 10000)),
                     limits = c(0, 35000),
                     expand = c(0, 0)) +
  coord_flip() +
  theme_ipsum_rc() +
  labs(y = "trennide arv",
       x = "spordiala",
       title = "Trennide ja treenijate arv kokku",
       subtitle = "TOP 30 spordiala\nSulgudes on spordialaga tegelenud inimeste arv")

Mis kell tehakse trenni nädalavahetusel?

top_trennid_nadalavahetusel <- trennid_toodeldud %>% 
  filter(spordiala %in% top_15_spordiala) %>%  # ainult top 15 alad
  left_join(spordiala_est) %>% 
  filter(!is.na(workout_date)) %>%  # välista ilma kellaajata trennid
  mutate(nadalapaev = weekdays(workout_date)) %>%  # leia trenni nädalapäev
  filter(nadalapaev %in% c("Saturday", "Sunday")) %>%  # ainult nädalavahetused
  thicken(by = "workout_date",  interval = "15 mins") %>%  # ümarda kõik kellaajad 15 min täpsusega
  # asenda kõik kuupäevad 01.01.2017
  # nii on lihtsam andmed ühele skaalale viia
  mutate(aeg = ymd_hms(str_replace(workout_date_15_min, "201.-..-..", "2017-01-01"))) %>% 
  select(spordiala_est, aeg) %>% 
  group_by(spordiala_est) %>%
  mutate(n = n()) %>% 
  filter(aeg > ymd_hms("2017-01-01 06:00:00")) %>%  # ainult pärast kl 6 tehtud trennid
  ungroup() %>% 
  mutate(n_total = n(),  # kogu trenni arvu kuvamiseks
         spordiala_est = fct_reorder(spordiala_est, n)) %>% 
  arrange(spordiala_est, aeg)
Joining, by = "spordiala"
Datetime variable was unsorted, result will be unsorted as well.
top_trennid_nadalavahetusel %>% 
  ggplot(aes(aeg, spordiala_est)) +
  geom_joy(aes(fill = spordiala_est), scale = 2, colour = "white", size = 0.7) +
  theme_ipsum_rc() +
  scale_y_discrete(expand = c(0.01, 0)) +
  # formati x-teljel kellaaeg
  scale_x_datetime(labels = function(x) format(x, "%H:%M"),
                   breaks = seq(ymd_hms("2017-01-01 06:00:00"),
                                ymd_hms("2017-01-01 24:00:00"), "3 hours"),
                   expand = c(0, 0)) +
  scale_fill_cyclical(values = c("#3182bd", "#6baed6")) +  # vahelduvad värvitoonid
  labs(x = "kellaaeg",
       y = "spordiala",
       title = "Mis kell tehakse trenni nädalavahetusel?",
       subtitle = str_c("ca ", round(max(top_trennid_nadalavahetusel$n_total, na.rm = TRUE) / 1000, 0),
                        " 000 Endomondos logitud trenni põhjal"))

Mis kell tehakse trenni tööpäevadel?

top_trennid_toopaevadel <- trennid_toodeldud %>% 
  filter(spordiala %in% top_15_spordiala) %>% 
  left_join(spordiala_est) %>% 
  filter(!is.na(workout_date)) %>% 
  mutate(nadalapaev = weekdays(workout_date)) %>% 
  filter(!nadalapaev %in% c("Saturday", "Sunday")) %>% 
  thicken(by = "workout_date",  interval = "15 mins") %>% 
  mutate(aeg = ymd_hms(str_replace(workout_date_15_min, "201.-..-..", "2017-01-01"))) %>% 
  select(spordiala_est, aeg, profile) %>% 
  group_by(spordiala_est) %>%
  mutate(n = n()) %>% 
  select(-profile) %>% 
  filter(aeg > ymd_hms("2017-01-01 06:00:00")) %>% 
  ungroup() %>% 
  mutate(n_total = n(),
         spordiala_est = fct_reorder(spordiala_est, n)) %>% 
  arrange(spordiala_est, aeg)
Joining, by = "spordiala"
Datetime variable was unsorted, result will be unsorted as well.
top_trennid_toopaevadel %>% 
  ggplot(aes(aeg, spordiala_est)) +
  geom_joy(aes(fill = spordiala_est), scale = 2, colour = "white", size = 0.7) +
  # theme_joy() +
  theme_ipsum_rc() +
  scale_y_discrete(expand = c(0.01, 0)) +
  scale_x_datetime(labels = function(x) format(x, "%H:%M"),
                   breaks = seq(ymd_hms("2017-01-01 06:00:00"),
                                ymd_hms("2017-01-01 24:00:00"), "3 hours"),
                   expand = c(0, 0)) +
  scale_fill_cyclical(values = c("#3182bd", "#6baed6")) +
  labs(x = "kellaaeg",
       y = "spordiala",
       title = "Mis kell tehakse trenni tööpäevadel?",
       subtitle = str_c("ca ", round(max(top_trennid_toopaevadel$n_total, na.rm = TRUE) / 1000, 0),
                        " 000 Endomondos logitud trenni põhjal"))

Mis nädalapäevadel mingit spordiala harrastatakse?

top_trennid_paevade_loikes <- trennid_toodeldud %>% 
  filter(spordiala %in% top_15_spordiala) %>% 
  left_join(spordiala_est) %>% 
  filter(!is.na(workout_date)) %>% 
  # nädalapäeva number (1 = esmaspäev)
  mutate(nadalapaev = wday(workout_date, week_start = 1)) %>% 
  select(spordiala_est, nadalapaev, profile) %>% 
  group_by(spordiala_est) %>%
  mutate(n = n()) %>% 
  select(-profile) %>% 
  ungroup() %>% 
  mutate(n_total = n(),
         spordiala_est = fct_reorder(spordiala_est, n)) %>% 
  arrange(spordiala_est, nadalapaev)
Joining, by = "spordiala"
top_trennid_paevade_loikes %>% 
  ggplot(aes(nadalapaev, spordiala_est)) +
  geom_joy(aes(fill = spordiala_est), scale = 2, colour = "white", size = 0.7) +
  geom_vline(xintercept = 5.5) +
  annotate("text", x = 6.5, y = 16.5, label = "nädalavahetuse") +
  theme_ipsum_rc() +
  scale_y_discrete(expand = c(0.01, 0)) +
  scale_x_continuous(breaks = seq(1, 7, by = 1),
                     labels = c("esmasp", "teisip", "kolmap", "neljap", "reede", "laup", "pühap"),
                     expand = c(0, 0)) +
  scale_fill_cyclical(values = c("#3182bd", "#6baed6")) +
  labs(x = "päev",
       y = "spordiala",
       title = "Mis päeval spordiala harrastatakse?",
       subtitle = str_c("ca ", round(max(top_trennid_paevade_loikes$n_total, na.rm = TRUE) / 1000, 0),
                        " 000 Endomondos logitud trenni põhjal"))

Kuidas jagunevad spordialad trenni kestvuse lõikes?

top_trennid_kestvus <- trennid_toodeldud %>% 
  # arvuta iga trenni pikkus minutites
  mutate(kestvus_minutites = as.numeric(as.duration(trenni_kestvus)) / 60) %>% 
  filter(spordiala %in% top_15_spordiala) %>% 
  left_join(spordiala_est) %>% 
  filter(!is.na(workout_date),
         kestvus_minutites <= 120) %>%  # max 2 H pikad trennid 
  select(spordiala_est, kestvus_minutites, profile) %>% 
  group_by(spordiala_est) %>%
  mutate(n = n()) %>% 
  select(-profile) %>% 
  ungroup() %>% 
  mutate(n_total = n(),
         spordiala_est = fct_reorder(spordiala_est, n)) %>% 
  arrange(spordiala_est, kestvus_minutites)
longer object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object lengthJoining, by = "spordiala"
top_trennid_kestvus %>% 
  ggplot(aes(kestvus_minutites, spordiala_est)) +
  geom_joy(aes(fill = spordiala_est), scale = 2, colour = "white", size = 0.7) +
  theme_ipsum_rc() +
  scale_y_discrete(expand = c(0.01, 0)) +
  scale_x_continuous(breaks = seq(0, 120, by = 30),
                     expand = c(0, 0)) +
  scale_fill_cyclical(values = c("#3182bd", "#6baed6")) +
  labs(x = "kestvus, min",
       y = "spordiala",
       title = "Kui pikalt trenni tehakse?",
       subtitle = str_c("ca ", round(max(top_trennid_kestvus$n_total, na.rm = TRUE) / 1000, 0),
                        " 000 Endomondos logitud trenni põhjal"))

Kuidas jagunevad spordialade lõikes inimeste vanus?

top_trennid_vanus <- trennid_toodeldud %>% 
  filter(spordiala %in% top_15_spordiala) %>% 
  left_join(spordiala_est) %>% 
  mutate(vanus_praegu = round(as.numeric(difftime(today(), synnipaev, units = "days")) / 365, 0)) %>% 
  filter(!is.na(synnipaev), 
         vanus_praegu >= 10,
         vanus_praegu <= 70) %>%
  select(spordiala_est, vanus_praegu, profile) %>% 
  group_by(spordiala_est) %>%
  mutate(n = n()) %>% 
  distinct(profile, spordiala_est, .keep_all = TRUE) %>% 
  ungroup() %>% 
  mutate(n_total = n_distinct(profile),
         spordiala_est = fct_reorder(spordiala_est, n)) %>% 
  select(-profile) %>% 
  arrange(spordiala_est, vanus_praegu)
Joining, by = "spordiala"
top_trennid_vanus %>% 
  ggplot(aes(vanus_praegu, spordiala_est)) +
  geom_joy(aes(fill = spordiala_est), scale = 2, colour = "white", size = 0.7) +
  theme_ipsum_rc() +
  scale_y_discrete(expand = c(0.01, 0)) +
  scale_x_continuous(breaks = seq(10, 70, by = 10),
                     limits = c(10, 70),
                     expand = c(0, 0)) +
  scale_fill_cyclical(values = c("#3182bd", "#6baed6")) +
  labs(x = "vanus",
       y = "spordiala",
       title = "Mis vanuses trenni tehakse?",
       subtitle = str_c("ca ", round(max(top_trennid_vanus$n_total, na.rm = TRUE) / 1000, 0),
                        " 000 Endomondo konto andmetel"))

Vanus ja lemmik spordiala.

top_trennid_lemmik_spordiala_vanus <- trennid_toodeldud %>% 
  # grupeeri mõned spordialad kokku
  mutate(lemmik_spordiala = case_when(.$lemmik_spordiala == "Walking (Fitness)" ~ "walking",
                                      .$lemmik_spordiala == "Skiing (Cross country)" ~ "skiing",
                                      .$lemmik_spordiala == "Football (Soccer)" ~ "soccer",
                                      TRUE ~ .$lemmik_spordiala),
         lemmik_spordiala = str_to_lower(lemmik_spordiala)) %>% 
  filter(lemmik_spordiala %in% top_10_lemmik_spordiala) %>% 
  left_join(spordiala_est, by = c("lemmik_spordiala" = "spordiala")) %>% 
  mutate(vanus_praegu = round(as.numeric(difftime(today(), synnipaev, units = "days")) / 365, 0)) %>% 
  filter(!is.na(synnipaev), 
         vanus_praegu >= 10,
         vanus_praegu <= 70,
         !is.na(lemmik_spordiala)) %>%
  select(lemmik_spordiala_est = spordiala_est, vanus_praegu, profile) %>% 
  group_by(lemmik_spordiala_est) %>%
  mutate(n = n()) %>% 
  # iga kasutaja ühekordselt
  distinct(profile, lemmik_spordiala_est, .keep_all = TRUE) %>% 
  ungroup() %>% 
  mutate(n_total = n_distinct(profile),
         lemmik_spordiala_est = fct_reorder(lemmik_spordiala_est, n)) %>% 
  select(-profile) %>% 
  arrange(lemmik_spordiala_est, vanus_praegu)
top_trennid_lemmik_spordiala_vanus %>% 
  ggplot(aes(vanus_praegu, lemmik_spordiala_est)) +
  geom_joy(aes(fill = lemmik_spordiala_est), scale = 2, colour = "white", size = 0.7) +
  theme_ipsum_rc() +
  scale_y_discrete(expand = c(0.01, 0)) +
  scale_x_continuous(breaks = seq(10, 70, by = 10),
                     limits = c(10, 70),
                     expand = c(0, 0)) +
  scale_fill_cyclical(values = c("#3182bd", "#6baed6")) +
  labs(x = "vanus",
       y = "lemmik spordiala",
       title = "Lemmik spordiala vs vanus",
       subtitle = str_c("ca ", round(max(top_trennid_lemmik_spordiala_vanus$n_total, na.rm = TRUE) / 1000, 0),
                        " 000 Endomondo konto andmetel"))

Kuidas mõjutab vanus mediaan keskmist jooksu kiirust?

trennid_toodeldud %>% 
  ungroup() %>% 
  mutate(kestvus_minutites = as.numeric(as.duration(trenni_kestvus)) / 60) %>% 
  filter(!is.na(synnipaev), 
         spordiala == "running", 
         !is.na(trenni_kestvus),
         !is.na(sugu)) %>% 
  mutate(vanus_praegu = as.numeric(difftime(today(), synnipaev, units = "days")) / 365,
         kiirus = kestvus_minutites / distants_km) %>% 
  # filter(kiirus < 15, kiirus > 1) %>%
  group_by(profile, vanus_praegu, sugu) %>%
  summarise(kiirus_median = median(kiirus, na.rm = TRUE),
            n = n()) %>% 
  filter(n >= 10, kiirus_median <= 10, kiirus_median >= 3) %>% 
  ungroup() %>% 
  ggplot(aes(vanus_praegu, kiirus_median, colour = sugu)) +
  geom_point() +
  theme_ipsum_rc() +
  labs(x = "vanus",
       y = "tempo min/km",
       title = "Keskmise jooksu tempo ja vanuse seos",
       subtitle = "Iga punkt tähistab ühe inimese mediaan tempot")
longer object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object length

Milline on vanuse ja mediaan keskmine joksu distantsi vaheline seos?

trennid_toodeldud %>% 
  ungroup() %>% 
  filter(!is.na(synnipaev), 
         spordiala == "running", 
         !is.na(distants_km),
         !is.na(sugu)) %>% 
  mutate(vanus_praegu = as.numeric(difftime(today(), synnipaev, units = "days")) / 365) %>% 
  group_by(profile, vanus_praegu, sugu) %>%
  summarise(distance_median = median(distants_km, na.rm = TRUE),
            n = n()) %>% 
  filter(n >= 10, vanus_praegu >= 10, distance_median <= 15) %>% 
  ungroup() %>% 
  ggplot(aes(vanus_praegu, distance_median, colour = sugu)) +
  geom_point() +
  theme_ipsum_rc() +
  labs(x = "vanus",
       y = "distants, km",
       title = "Keskmise jooksu distantsi ja vanuse seos",
       subtitle = "Iga punkt tähistab ühe inimese mediaan distantsi")

LS0tCnRpdGxlOiAiVmlzdWFsaXNlZXJpIHRyZW5uaSBhbmRtZWlkIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoYW55dGltZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoc2tpbXIpCmxpYnJhcnkobGlzdHZpZXdlcikKbGlicmFyeShybGFuZykKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGdnam95KQpsaWJyYXJ5KHBhZHIpCmxpYnJhcnkoaHJicnRoZW1lcykKbGlicmFyeShqYW5pdG9yKQoKbG9hZCgib3V0cHV0L3RyZW5uaWRfdG9vZGVsZHVkLlJEYXRhIikKYGBgCgojIyMgVHV0dnUgYW5kbWV0ZWdhCmBgYHtyfQpnbGltcHNlKHRyZW5uaWRfdG9vZGVsZHVkKQpgYGAKCsOcbGV2YWFkZSBrw7VpZ2lzdCB2ZWVyZ3VkZXN0CmBgYHtyfQojIEZ1bmt0c2lvb24gbGVpYWIgdmFsaXR1ZCB2ZWVydSBrw7VpayB1bmlrYWFsc2VkIHbDpMOkcnR1c2VkIGtvb3MgZXNpbmVtaXNzYWdlZHVzZWdhCnVuaWthYWxzZWQgPC0gZnVuY3Rpb24oeCl7CiAgICB2YWxpayA8LSBzeW0oeCkKICAgIAogICAgcCA8LSB0cmVubmlkX3Rvb2RlbGR1ZCAlPiUgCiAgICAgICAgbXV0YXRlKHV1cyA9IHN0cl9yZXBsYWNlX25hKCEhdmFsaWspKSAlPiUgCiAgICAgICAgY291bnQodXVzKSAlPiUgCiAgICAgICAgbXV0YXRlKHV1cyA9IHN0cl9jKHV1cywgIiAoIiwgbiwgIikiKSwKICAgICAgICAgICAgICAgdXVzID0gZmN0X3Jlb3JkZXIodXVzLCBuKSkgJT4lIAogICAgICAgIGFycmFuZ2UoZGVzYyhuKSkgJT4lIAogICAgICAgIHNlbGVjdCh1dXMpCiAgICAKICAgIGNvbG5hbWVzKHApIDwtIHgKICAgIAogICAgcmV0dXJuKHApCn0KCiMga8O1aWsgdmVlcnVkLCBtaXMgcG9sZSBJRCB2w7VpIG11dWwgcMO1aGp1c2VsIHbDpGxpc3RhdHVkCnZlZXJ1ZCA8LSB0cmVubmlkX3Rvb2RlbGR1ZCAlPiUgCiAgICBzZWxlY3QoLXByb2ZpbGUsIC13b3Jrb3V0LCAtY29udGFpbnMoInJhdyIpKSAlPiUKICAgIGNvbG5hbWVzKCkKCiMgbGVpYSBkYXRhIGZyYW1lIGvDtWlnaSB2ZWVyZ3VkZSB1bmlrYWFsc2VkIHbDpMOkcnR1c2VkCnAgPC0gbWFwKHZlZXJ1ZCwgdW5pa2FhbHNlZCkKCiMga3V2YSB1bmlrYWFsc2VkIHbDpMOkcnR1c2VkCnAgJT4lIAogICAgbWFwKC4sIGFzLmxpc3QpICU+JSAKICAgIGZsYXR0ZW4oKSAlPiUgCiAgICBqc29uZWRpdCgpCmBgYAoKVE9QIDE1IHBvcHVsYWFyc2VtYXQgc3BvcmRpYWxhCmBgYHtyfQp0b3BfMTVfc3BvcmRpYWxhIDwtIHRyZW5uaWRfdG9vZGVsZHVkICU+JSAKICBmaWx0ZXIoc3BvcmRpYWxhICE9ICJleGVyY2lzaW5nIikgJT4lICAjIHbDpGxpc3RhIG1pdHRlbWlkYWdpw7x0bGV2ICJleGVyY2lzaW5nIgogIGNvdW50KHNwb3JkaWFsYSwgc29ydCA9IFRSVUUpICU+JSAKICBoZWFkKDE1KSAlPiUgCiAgLiRzcG9yZGlhbGEKCnRvcF8xNV9zcG9yZGlhbGEKYGBgCgpUT1AgMTAgbGVtbWlrIHNwb3JkaWFsYQpgYGB7cn0KdG9wXzEwX2xlbW1pa19zcG9yZGlhbGEgPC0gdHJlbm5pZF90b29kZWxkdWQgJT4lIAogIGRpc3RpbmN0KHByb2ZpbGUsIGxlbW1pa19zcG9yZGlhbGEpICU+JSAKICBtdXRhdGUobGVtbWlrX3Nwb3JkaWFsYSA9IGNhc2Vfd2hlbiguJGxlbW1pa19zcG9yZGlhbGEgPT0gIldhbGtpbmcgKEZpdG5lc3MpIiB+ICJ3YWxraW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuJGxlbW1pa19zcG9yZGlhbGEgPT0gIlNraWluZyAoQ3Jvc3MgY291bnRyeSkiIH4gInNraWluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLiRsZW1taWtfc3BvcmRpYWxhID09ICJGb290YmFsbCAoU29jY2VyKSIgfiAic29jY2VyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gLiRsZW1taWtfc3BvcmRpYWxhKSwKICAgICAgICAgbGVtbWlrX3Nwb3JkaWFsYSA9IHN0cl90b19sb3dlcihsZW1taWtfc3BvcmRpYWxhKSkgJT4lIAogIGNvdW50KGxlbW1pa19zcG9yZGlhbGEsIHNvcnQgPSBUUlVFKSAlPiUgCiAgaGVhZCgxMCkgJT4lIAogIC4kbGVtbWlrX3Nwb3JkaWFsYQoKdG9wXzEwX2xlbW1pa19zcG9yZGlhbGEKYGBgCgpQb3B1bGFhcnNlbWFkIHNwb3JkaWFsYWQgZWVzdGkga2VlbGVzCmBgYHtyfQpzcG9yZGlhbGFfZXN0IDwtIHRyaWJibGUoCiAgfnNwb3JkaWFsYSwgfnNwb3JkaWFsYV9lc3QsCiAgIndhbGtpbmciLCAia8OkaW1pbmUiLAogICJydW5uaW5nIiwgImpvb2tzbWluZSIsCiAgImN5Y2xpbmciLCAiamFsZ3JhdHRhc8O1aXQiLAogICJjeWNsaW5nIChzcG9ydCkiLCAiamFsZ3JhdHRhc8O1aXQgKHNwb3J0KSIsCiAgImN5Y2xpbmcgKHRyYW5zcG9ydCkiLCAiamFsZ3JhdHRhc8O1aXQgKHRyYW5zcG9ydCkiLAogICJtb3VudGFpbiBiaWtpbmciLCAibWFhc3Rpa3VyYXR0YSBzw7VpdCIsCiAgInNrYXRpbmciLCAicnVsbHVpc3V0YW1pbmUiLAogICJyb2xsZXIgc2thdGluZyIsICJydWxsdWlzdXRhbWluZSIsCiAgImRvaW5nIHdlaWdodCB0cmFpbmluZyIsICJqw7V1dHJlbm4iLCAgCiAgIndlaWdodCB0cmFpbmluZyIsICJqw7V1dHJlbm4iLCAgCiAgInNraWluZyIsICJzdXVzYXRhbWluZSIsICAKICAiaGlraW5nIiwgIm1hdGthbWluZSIsICAKICAiZGFuY2luZyIsICJ0YW50c2ltaW5lIiwgIAogICJkb2luZyBhZXJvYmljcyIsICJhZXJvb2Jpa2EiLAogICJkb2luZyBjaXJjdWl0IHRyYWluaW5nIiwgInJpbmd0cmVlbmluZyIsCiAgInN3aW1taW5nIiwgInVqdW1pbmUiLCAgCiAgIm9yaWVudGVlcmluZyIsICJvcmllbnRlZXJ1bWluZSIsICAKICAicm9sbGVyIHNraWluZyIsICJydWxsc3V1c2F0YW1pbmUiLCAgCiAgImdvbGZpbmciLCAiZ29sZiIsCiAgInNvY2NlciIsICJqYWxncGFsbCIKKQpgYGAKCgrDnGxldmFhZGUgYW5hbMO8w7xzaXMga2FzdXRhdHVkIHRyZW5uaWRlIGphIGluaW1ldHNlIGFydnVzdCAKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CnRyZW5uaWRfdG9vZGVsZHVkICU+JSAKICBmaWx0ZXIoIWlzLm5hKHNwb3JkaWFsYSkpICU+JSAKICBncm91cF9ieShzcG9yZGlhbGEpICU+JSAKICBzdW1tYXJpc2UodHJlbm5pZGVfYXJ2ID0gbigpLAogICAgICAgICAgICBrYXN1dGFqYXRlX2FydiA9IG5fZGlzdGluY3QocHJvZmlsZSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShsYWJlbCA9IHN0cl9jKHRyZW5uaWRlX2FydiwgIiAoIiwga2FzdXRhamF0ZV9hcnYsICIpIikpICU+JSAKICBhcnJhbmdlKGRlc2ModHJlbm5pZGVfYXJ2KSkgJT4lIAogIGhlYWQoMzApICU+JSAKICBnZ3Bsb3QoYWVzKGZjdF9yZW9yZGVyKHNwb3JkaWFsYSwgdHJlbm5pZGVfYXJ2KSwgdHJlbm5pZGVfYXJ2KSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBsYWJlbCksIGhqdXN0ID0gLTAuMSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSAoc2VxKDAsIDM1MDAwLCBieSA9IDEwMDAwKSksCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgMzUwMDApLAogICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBjKDAsIDApKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9pcHN1bV9yYygpICsKICBsYWJzKHkgPSAidHJlbm5pZGUgYXJ2IiwKICAgICAgIHggPSAic3BvcmRpYWxhIiwKICAgICAgIHRpdGxlID0gIlRyZW5uaWRlIGphIHRyZWVuaWphdGUgYXJ2IGtva2t1IiwKICAgICAgIHN1YnRpdGxlID0gIlRPUCAzMCBzcG9yZGlhbGFcblN1bGd1ZGVzIG9uIHNwb3JkaWFsYWdhIHRlZ2VsZW51ZCBpbmltZXN0ZSBhcnYiKQoKYGBgCgoKCk1pcyBrZWxsIHRlaGFrc2UgdHJlbm5pIG7DpGRhbGF2YWhldHVzZWw/CmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOH0KdG9wX3RyZW5uaWRfbmFkYWxhdmFoZXR1c2VsIDwtIHRyZW5uaWRfdG9vZGVsZHVkICU+JSAKICBmaWx0ZXIoc3BvcmRpYWxhICVpbiUgdG9wXzE1X3Nwb3JkaWFsYSkgJT4lICAjIGFpbnVsdCB0b3AgMTUgYWxhZAogIGxlZnRfam9pbihzcG9yZGlhbGFfZXN0KSAlPiUgCiAgZmlsdGVyKCFpcy5uYSh3b3Jrb3V0X2RhdGUpKSAlPiUgICMgdsOkbGlzdGEgaWxtYSBrZWxsYWFqYXRhIHRyZW5uaWQKICBtdXRhdGUobmFkYWxhcGFldiA9IHdlZWtkYXlzKHdvcmtvdXRfZGF0ZSkpICU+JSAgIyBsZWlhIHRyZW5uaSBuw6RkYWxhcMOkZXYKICBmaWx0ZXIobmFkYWxhcGFldiAlaW4lIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIpKSAlPiUgICMgYWludWx0IG7DpGRhbGF2YWhldHVzZWQKICB0aGlja2VuKGJ5ID0gIndvcmtvdXRfZGF0ZSIsICBpbnRlcnZhbCA9ICIxNSBtaW5zIikgJT4lICAjIMO8bWFyZGEga8O1aWsga2VsbGFhamFkIDE1IG1pbiB0w6Rwc3VzZWdhCiAgIyBhc2VuZGEga8O1aWsga3V1cMOkZXZhZCAwMS4wMS4yMDE3CiAgIyBuaWkgb24gbGlodHNhbSBhbmRtZWQgw7xoZWxlIHNrYWFsYWxlIHZpaWEKICBtdXRhdGUoYWVnID0geW1kX2htcyhzdHJfcmVwbGFjZSh3b3Jrb3V0X2RhdGVfMTVfbWluLCAiMjAxLi0uLi0uLiIsICIyMDE3LTAxLTAxIikpKSAlPiUgCiAgc2VsZWN0KHNwb3JkaWFsYV9lc3QsIGFlZykgJT4lIAogIGdyb3VwX2J5KHNwb3JkaWFsYV9lc3QpICU+JQogIG11dGF0ZShuID0gbigpKSAlPiUgCiAgZmlsdGVyKGFlZyA+IHltZF9obXMoIjIwMTctMDEtMDEgMDY6MDA6MDAiKSkgJT4lICAjIGFpbnVsdCBww6RyYXN0IGtsIDYgdGVodHVkIHRyZW5uaWQKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShuX3RvdGFsID0gbigpLCAgIyBrb2d1IHRyZW5uaSBhcnZ1IGt1dmFtaXNla3MKICAgICAgICAgc3BvcmRpYWxhX2VzdCA9IGZjdF9yZW9yZGVyKHNwb3JkaWFsYV9lc3QsIG4pKSAlPiUgCiAgYXJyYW5nZShzcG9yZGlhbGFfZXN0LCBhZWcpCgoKdG9wX3RyZW5uaWRfbmFkYWxhdmFoZXR1c2VsICU+JSAKICBnZ3Bsb3QoYWVzKGFlZywgc3BvcmRpYWxhX2VzdCkpICsKICBnZW9tX2pveShhZXMoZmlsbCA9IHNwb3JkaWFsYV9lc3QpLCBzY2FsZSA9IDIsIGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjcpICsKICB0aGVtZV9pcHN1bV9yYygpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMC4wMSwgMCkpICsKICAjIGZvcm1hdGkgeC10ZWxqZWwga2VsbGFhZWcKICBzY2FsZV94X2RhdGV0aW1lKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGZvcm1hdCh4LCAiJUg6JU0iKSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSh5bWRfaG1zKCIyMDE3LTAxLTAxIDA2OjAwOjAwIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeW1kX2htcygiMjAxNy0wMS0wMSAyNDowMDowMCIpLCAiMyBob3VycyIpLAogICAgICAgICAgICAgICAgICAgZXhwYW5kID0gYygwLCAwKSkgKwogIHNjYWxlX2ZpbGxfY3ljbGljYWwodmFsdWVzID0gYygiIzMxODJiZCIsICIjNmJhZWQ2IikpICsgICMgdmFoZWxkdXZhZCB2w6Rydml0b29uaWQKICBsYWJzKHggPSAia2VsbGFhZWciLAogICAgICAgeSA9ICJzcG9yZGlhbGEiLAogICAgICAgdGl0bGUgPSAiTWlzIGtlbGwgdGVoYWtzZSB0cmVubmkgbsOkZGFsYXZhaGV0dXNlbD8iLAogICAgICAgc3VidGl0bGUgPSBzdHJfYygiY2EgIiwgcm91bmQobWF4KHRvcF90cmVubmlkX25hZGFsYXZhaGV0dXNlbCRuX3RvdGFsLCBuYS5ybSA9IFRSVUUpIC8gMTAwMCwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICIgMDAwIEVuZG9tb25kb3MgbG9naXR1ZCB0cmVubmkgcMO1aGphbCIpKQoKYGBgCgpNaXMga2VsbCB0ZWhha3NlIHRyZW5uaSB0w7bDtnDDpGV2YWRlbD8KYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4fQp0b3BfdHJlbm5pZF90b29wYWV2YWRlbCA8LSB0cmVubmlkX3Rvb2RlbGR1ZCAlPiUgCiAgZmlsdGVyKHNwb3JkaWFsYSAlaW4lIHRvcF8xNV9zcG9yZGlhbGEpICU+JSAKICBsZWZ0X2pvaW4oc3BvcmRpYWxhX2VzdCkgJT4lIAogIGZpbHRlcighaXMubmEod29ya291dF9kYXRlKSkgJT4lIAogIG11dGF0ZShuYWRhbGFwYWV2ID0gd2Vla2RheXMod29ya291dF9kYXRlKSkgJT4lIAogIGZpbHRlcighbmFkYWxhcGFldiAlaW4lIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIpKSAlPiUgCiAgdGhpY2tlbihieSA9ICJ3b3Jrb3V0X2RhdGUiLCAgaW50ZXJ2YWwgPSAiMTUgbWlucyIpICU+JSAKICBtdXRhdGUoYWVnID0geW1kX2htcyhzdHJfcmVwbGFjZSh3b3Jrb3V0X2RhdGVfMTVfbWluLCAiMjAxLi0uLi0uLiIsICIyMDE3LTAxLTAxIikpKSAlPiUgCiAgc2VsZWN0KHNwb3JkaWFsYV9lc3QsIGFlZywgcHJvZmlsZSkgJT4lIAogIGdyb3VwX2J5KHNwb3JkaWFsYV9lc3QpICU+JQogIG11dGF0ZShuID0gbigpKSAlPiUgCiAgc2VsZWN0KC1wcm9maWxlKSAlPiUgCiAgZmlsdGVyKGFlZyA+IHltZF9obXMoIjIwMTctMDEtMDEgMDY6MDA6MDAiKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKG5fdG90YWwgPSBuKCksCiAgICAgICAgIHNwb3JkaWFsYV9lc3QgPSBmY3RfcmVvcmRlcihzcG9yZGlhbGFfZXN0LCBuKSkgJT4lIAogIGFycmFuZ2Uoc3BvcmRpYWxhX2VzdCwgYWVnKQoKCnRvcF90cmVubmlkX3Rvb3BhZXZhZGVsICU+JSAKICBnZ3Bsb3QoYWVzKGFlZywgc3BvcmRpYWxhX2VzdCkpICsKICBnZW9tX2pveShhZXMoZmlsbCA9IHNwb3JkaWFsYV9lc3QpLCBzY2FsZSA9IDIsIGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjcpICsKICB0aGVtZV9pcHN1bV9yYygpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMC4wMSwgMCkpICsKICBzY2FsZV94X2RhdGV0aW1lKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGZvcm1hdCh4LCAiJUg6JU0iKSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSh5bWRfaG1zKCIyMDE3LTAxLTAxIDA2OjAwOjAwIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeW1kX2htcygiMjAxNy0wMS0wMSAyNDowMDowMCIpLCAiMyBob3VycyIpLAogICAgICAgICAgICAgICAgICAgZXhwYW5kID0gYygwLCAwKSkgKwogIHNjYWxlX2ZpbGxfY3ljbGljYWwodmFsdWVzID0gYygiIzMxODJiZCIsICIjNmJhZWQ2IikpICsKICBsYWJzKHggPSAia2VsbGFhZWciLAogICAgICAgeSA9ICJzcG9yZGlhbGEiLAogICAgICAgdGl0bGUgPSAiTWlzIGtlbGwgdGVoYWtzZSB0cmVubmkgdMO2w7Zww6RldmFkZWw/IiwKICAgICAgIHN1YnRpdGxlID0gc3RyX2MoImNhICIsIHJvdW5kKG1heCh0b3BfdHJlbm5pZF90b29wYWV2YWRlbCRuX3RvdGFsLCBuYS5ybSA9IFRSVUUpIC8gMTAwMCwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICIgMDAwIEVuZG9tb25kb3MgbG9naXR1ZCB0cmVubmkgcMO1aGphbCIpKQoKYGBgCgpNaXMgbsOkZGFsYXDDpGV2YWRlbCBtaW5naXQgc3BvcmRpYWxhIGhhcnJhc3RhdGFrc2U/CmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOH0KdG9wX3RyZW5uaWRfcGFldmFkZV9sb2lrZXMgPC0gdHJlbm5pZF90b29kZWxkdWQgJT4lIAogIGZpbHRlcihzcG9yZGlhbGEgJWluJSB0b3BfMTVfc3BvcmRpYWxhKSAlPiUgCiAgbGVmdF9qb2luKHNwb3JkaWFsYV9lc3QpICU+JSAKICBmaWx0ZXIoIWlzLm5hKHdvcmtvdXRfZGF0ZSkpICU+JSAKICAjIG7DpGRhbGFww6RldmEgbnVtYmVyICgxID0gZXNtYXNww6RldikKICBtdXRhdGUobmFkYWxhcGFldiA9IHdkYXkod29ya291dF9kYXRlLCB3ZWVrX3N0YXJ0ID0gMSkpICU+JSAKICBzZWxlY3Qoc3BvcmRpYWxhX2VzdCwgbmFkYWxhcGFldiwgcHJvZmlsZSkgJT4lIAogIGdyb3VwX2J5KHNwb3JkaWFsYV9lc3QpICU+JQogIG11dGF0ZShuID0gbigpKSAlPiUgCiAgc2VsZWN0KC1wcm9maWxlKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUobl90b3RhbCA9IG4oKSwKICAgICAgICAgc3BvcmRpYWxhX2VzdCA9IGZjdF9yZW9yZGVyKHNwb3JkaWFsYV9lc3QsIG4pKSAlPiUgCiAgYXJyYW5nZShzcG9yZGlhbGFfZXN0LCBuYWRhbGFwYWV2KQoKCnRvcF90cmVubmlkX3BhZXZhZGVfbG9pa2VzICU+JSAKICBnZ3Bsb3QoYWVzKG5hZGFsYXBhZXYsIHNwb3JkaWFsYV9lc3QpKSArCiAgZ2VvbV9qb3koYWVzKGZpbGwgPSBzcG9yZGlhbGFfZXN0KSwgc2NhbGUgPSAyLCBjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC43KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNS41KSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gNi41LCB5ID0gMTYuNSwgbGFiZWwgPSAibsOkZGFsYXZhaGV0dXNlIikgKwogIHRoZW1lX2lwc3VtX3JjKCkgKwogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLjAxLCAwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMSwgNywgYnkgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiZXNtYXNwIiwgInRlaXNpcCIsICJrb2xtYXAiLCAibmVsamFwIiwgInJlZWRlIiwgImxhdXAiLCAicMO8aGFwIiksCiAgICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV9maWxsX2N5Y2xpY2FsKHZhbHVlcyA9IGMoIiMzMTgyYmQiLCAiIzZiYWVkNiIpKSArCiAgbGFicyh4ID0gInDDpGV2IiwKICAgICAgIHkgPSAic3BvcmRpYWxhIiwKICAgICAgIHRpdGxlID0gIk1pcyBww6RldmFsIHNwb3JkaWFsYSBoYXJyYXN0YXRha3NlPyIsCiAgICAgICBzdWJ0aXRsZSA9IHN0cl9jKCJjYSAiLCByb3VuZChtYXgodG9wX3RyZW5uaWRfcGFldmFkZV9sb2lrZXMkbl90b3RhbCwgbmEucm0gPSBUUlVFKSAvIDEwMDAsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAiIDAwMCBFbmRvbW9uZG9zIGxvZ2l0dWQgdHJlbm5pIHDDtWhqYWwiKSkKYGBgCgoKS3VpZGFzIGphZ3VuZXZhZCBzcG9yZGlhbGFkIHRyZW5uaSBrZXN0dnVzZSBsw7Vpa2VzPwpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9CnRvcF90cmVubmlkX2tlc3R2dXMgPC0gdHJlbm5pZF90b29kZWxkdWQgJT4lIAogICMgYXJ2dXRhIGlnYSB0cmVubmkgcGlra3VzIG1pbnV0aXRlcwogIG11dGF0ZShrZXN0dnVzX21pbnV0aXRlcyA9IGFzLm51bWVyaWMoYXMuZHVyYXRpb24odHJlbm5pX2tlc3R2dXMpKSAvIDYwKSAlPiUgCiAgZmlsdGVyKHNwb3JkaWFsYSAlaW4lIHRvcF8xNV9zcG9yZGlhbGEpICU+JSAKICBsZWZ0X2pvaW4oc3BvcmRpYWxhX2VzdCkgJT4lIAogIGZpbHRlcighaXMubmEod29ya291dF9kYXRlKSwKICAgICAgICAga2VzdHZ1c19taW51dGl0ZXMgPD0gMTIwKSAlPiUgICMgbWF4IDIgSCBwaWthZCB0cmVubmlkIAogIHNlbGVjdChzcG9yZGlhbGFfZXN0LCBrZXN0dnVzX21pbnV0aXRlcywgcHJvZmlsZSkgJT4lIAogIGdyb3VwX2J5KHNwb3JkaWFsYV9lc3QpICU+JQogIG11dGF0ZShuID0gbigpKSAlPiUgCiAgc2VsZWN0KC1wcm9maWxlKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUobl90b3RhbCA9IG4oKSwKICAgICAgICAgc3BvcmRpYWxhX2VzdCA9IGZjdF9yZW9yZGVyKHNwb3JkaWFsYV9lc3QsIG4pKSAlPiUgCiAgYXJyYW5nZShzcG9yZGlhbGFfZXN0LCBrZXN0dnVzX21pbnV0aXRlcykKCgp0b3BfdHJlbm5pZF9rZXN0dnVzICU+JSAKICBnZ3Bsb3QoYWVzKGtlc3R2dXNfbWludXRpdGVzLCBzcG9yZGlhbGFfZXN0KSkgKwogIGdlb21fam95KGFlcyhmaWxsID0gc3BvcmRpYWxhX2VzdCksIHNjYWxlID0gMiwgY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDAuNykgKwogIHRoZW1lX2lwc3VtX3JjKCkgKwogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLjAxLCAwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTIwLCBieSA9IDMwKSwKICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gYygwLCAwKSkgKwogIHNjYWxlX2ZpbGxfY3ljbGljYWwodmFsdWVzID0gYygiIzMxODJiZCIsICIjNmJhZWQ2IikpICsKICBsYWJzKHggPSAia2VzdHZ1cywgbWluIiwKICAgICAgIHkgPSAic3BvcmRpYWxhIiwKICAgICAgIHRpdGxlID0gIkt1aSBwaWthbHQgdHJlbm5pIHRlaGFrc2U/IiwKICAgICAgIHN1YnRpdGxlID0gc3RyX2MoImNhICIsIHJvdW5kKG1heCh0b3BfdHJlbm5pZF9rZXN0dnVzJG5fdG90YWwsIG5hLnJtID0gVFJVRSkgLyAxMDAwLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgIiAwMDAgRW5kb21vbmRvcyBsb2dpdHVkIHRyZW5uaSBww7VoamFsIikpCmBgYAoKCkt1aWRhcyBqYWd1bmV2YWQgc3BvcmRpYWxhZGUgbMO1aWtlcyBpbmltZXN0ZSB2YW51cz8KYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4fQp0b3BfdHJlbm5pZF92YW51cyA8LSB0cmVubmlkX3Rvb2RlbGR1ZCAlPiUgCiAgZmlsdGVyKHNwb3JkaWFsYSAlaW4lIHRvcF8xNV9zcG9yZGlhbGEpICU+JSAKICBsZWZ0X2pvaW4oc3BvcmRpYWxhX2VzdCkgJT4lIAogIG11dGF0ZSh2YW51c19wcmFlZ3UgPSByb3VuZChhcy5udW1lcmljKGRpZmZ0aW1lKHRvZGF5KCksIHN5bm5pcGFldiwgdW5pdHMgPSAiZGF5cyIpKSAvIDM2NSwgMCkpICU+JSAKICBmaWx0ZXIoIWlzLm5hKHN5bm5pcGFldiksIAogICAgICAgICB2YW51c19wcmFlZ3UgPj0gMTAsCiAgICAgICAgIHZhbnVzX3ByYWVndSA8PSA3MCkgJT4lCiAgc2VsZWN0KHNwb3JkaWFsYV9lc3QsIHZhbnVzX3ByYWVndSwgcHJvZmlsZSkgJT4lIAogIGdyb3VwX2J5KHNwb3JkaWFsYV9lc3QpICU+JQogIG11dGF0ZShuID0gbigpKSAlPiUgCiAgZGlzdGluY3QocHJvZmlsZSwgc3BvcmRpYWxhX2VzdCwgLmtlZXBfYWxsID0gVFJVRSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKG5fdG90YWwgPSBuX2Rpc3RpbmN0KHByb2ZpbGUpLAogICAgICAgICBzcG9yZGlhbGFfZXN0ID0gZmN0X3Jlb3JkZXIoc3BvcmRpYWxhX2VzdCwgbikpICU+JSAKICBzZWxlY3QoLXByb2ZpbGUpICU+JSAKICBhcnJhbmdlKHNwb3JkaWFsYV9lc3QsIHZhbnVzX3ByYWVndSkKCgp0b3BfdHJlbm5pZF92YW51cyAlPiUgCiAgZ2dwbG90KGFlcyh2YW51c19wcmFlZ3UsIHNwb3JkaWFsYV9lc3QpKSArCiAgZ2VvbV9qb3koYWVzKGZpbGwgPSBzcG9yZGlhbGFfZXN0KSwgc2NhbGUgPSAyLCBjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC43KSArCiAgdGhlbWVfaXBzdW1fcmMoKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBjKDAuMDEsIDApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxMCwgNzAsIGJ5ID0gMTApLAogICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDEwLCA3MCksCiAgICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV9maWxsX2N5Y2xpY2FsKHZhbHVlcyA9IGMoIiMzMTgyYmQiLCAiIzZiYWVkNiIpKSArCiAgbGFicyh4ID0gInZhbnVzIiwKICAgICAgIHkgPSAic3BvcmRpYWxhIiwKICAgICAgIHRpdGxlID0gIk1pcyB2YW51c2VzIHRyZW5uaSB0ZWhha3NlPyIsCiAgICAgICBzdWJ0aXRsZSA9IHN0cl9jKCJjYSAiLCByb3VuZChtYXgodG9wX3RyZW5uaWRfdmFudXMkbl90b3RhbCwgbmEucm0gPSBUUlVFKSAvIDEwMDAsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAiIDAwMCBFbmRvbW9uZG8ga29udG8gYW5kbWV0ZWwiKSkKYGBgCgpWYW51cyBqYSBsZW1taWsgc3BvcmRpYWxhLgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDd9CnRvcF90cmVubmlkX2xlbW1pa19zcG9yZGlhbGFfdmFudXMgPC0gdHJlbm5pZF90b29kZWxkdWQgJT4lIAogICMgZ3J1cGVlcmkgbcO1bmVkIHNwb3JkaWFsYWQga29ra3UKICBtdXRhdGUobGVtbWlrX3Nwb3JkaWFsYSA9IGNhc2Vfd2hlbiguJGxlbW1pa19zcG9yZGlhbGEgPT0gIldhbGtpbmcgKEZpdG5lc3MpIiB+ICJ3YWxraW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuJGxlbW1pa19zcG9yZGlhbGEgPT0gIlNraWluZyAoQ3Jvc3MgY291bnRyeSkiIH4gInNraWluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLiRsZW1taWtfc3BvcmRpYWxhID09ICJGb290YmFsbCAoU29jY2VyKSIgfiAic29jY2VyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gLiRsZW1taWtfc3BvcmRpYWxhKSwKICAgICAgICAgbGVtbWlrX3Nwb3JkaWFsYSA9IHN0cl90b19sb3dlcihsZW1taWtfc3BvcmRpYWxhKSkgJT4lIAogIGZpbHRlcihsZW1taWtfc3BvcmRpYWxhICVpbiUgdG9wXzEwX2xlbW1pa19zcG9yZGlhbGEpICU+JSAKICBsZWZ0X2pvaW4oc3BvcmRpYWxhX2VzdCwgYnkgPSBjKCJsZW1taWtfc3BvcmRpYWxhIiA9ICJzcG9yZGlhbGEiKSkgJT4lIAogIG11dGF0ZSh2YW51c19wcmFlZ3UgPSByb3VuZChhcy5udW1lcmljKGRpZmZ0aW1lKHRvZGF5KCksIHN5bm5pcGFldiwgdW5pdHMgPSAiZGF5cyIpKSAvIDM2NSwgMCkpICU+JSAKICBmaWx0ZXIoIWlzLm5hKHN5bm5pcGFldiksIAogICAgICAgICB2YW51c19wcmFlZ3UgPj0gMTAsCiAgICAgICAgIHZhbnVzX3ByYWVndSA8PSA3MCwKICAgICAgICAgIWlzLm5hKGxlbW1pa19zcG9yZGlhbGEpKSAlPiUKICBzZWxlY3QobGVtbWlrX3Nwb3JkaWFsYV9lc3QgPSBzcG9yZGlhbGFfZXN0LCB2YW51c19wcmFlZ3UsIHByb2ZpbGUpICU+JSAKICBncm91cF9ieShsZW1taWtfc3BvcmRpYWxhX2VzdCkgJT4lCiAgbXV0YXRlKG4gPSBuKCkpICU+JSAKICAjIGlnYSBrYXN1dGFqYSDDvGhla29yZHNlbHQKICBkaXN0aW5jdChwcm9maWxlLCBsZW1taWtfc3BvcmRpYWxhX2VzdCwgLmtlZXBfYWxsID0gVFJVRSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKG5fdG90YWwgPSBuX2Rpc3RpbmN0KHByb2ZpbGUpLAogICAgICAgICBsZW1taWtfc3BvcmRpYWxhX2VzdCA9IGZjdF9yZW9yZGVyKGxlbW1pa19zcG9yZGlhbGFfZXN0LCBuKSkgJT4lIAogIHNlbGVjdCgtcHJvZmlsZSkgJT4lIAogIGFycmFuZ2UobGVtbWlrX3Nwb3JkaWFsYV9lc3QsIHZhbnVzX3ByYWVndSkKCgp0b3BfdHJlbm5pZF9sZW1taWtfc3BvcmRpYWxhX3ZhbnVzICU+JSAKICBnZ3Bsb3QoYWVzKHZhbnVzX3ByYWVndSwgbGVtbWlrX3Nwb3JkaWFsYV9lc3QpKSArCiAgZ2VvbV9qb3koYWVzKGZpbGwgPSBsZW1taWtfc3BvcmRpYWxhX2VzdCksIHNjYWxlID0gMiwgY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDAuNykgKwogIHRoZW1lX2lwc3VtX3JjKCkgKwogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLjAxLCAwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTAsIDcwLCBieSA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygxMCwgNzApLAogICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfZmlsbF9jeWNsaWNhbCh2YWx1ZXMgPSBjKCIjMzE4MmJkIiwgIiM2YmFlZDYiKSkgKwogIGxhYnMoeCA9ICJ2YW51cyIsCiAgICAgICB5ID0gImxlbW1payBzcG9yZGlhbGEiLAogICAgICAgdGl0bGUgPSAiTGVtbWlrIHNwb3JkaWFsYSB2cyB2YW51cyIsCiAgICAgICBzdWJ0aXRsZSA9IHN0cl9jKCJjYSAiLCByb3VuZChtYXgodG9wX3RyZW5uaWRfbGVtbWlrX3Nwb3JkaWFsYV92YW51cyRuX3RvdGFsLCBuYS5ybSA9IFRSVUUpIC8gMTAwMCwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICIgMDAwIEVuZG9tb25kbyBrb250byBhbmRtZXRlbCIpKQpgYGAKCgpLdWlkYXMgbcO1anV0YWIgdmFudXMgbWVkaWFhbiBrZXNrbWlzdCBqb29rc3Uga2lpcnVzdD8KYGBge3J9CnRyZW5uaWRfdG9vZGVsZHVkICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShrZXN0dnVzX21pbnV0aXRlcyA9IGFzLm51bWVyaWMoYXMuZHVyYXRpb24odHJlbm5pX2tlc3R2dXMpKSAvIDYwKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShzeW5uaXBhZXYpLCAKICAgICAgICAgc3BvcmRpYWxhID09ICJydW5uaW5nIiwgCiAgICAgICAgICFpcy5uYSh0cmVubmlfa2VzdHZ1cyksCiAgICAgICAgICFpcy5uYShzdWd1KSkgJT4lIAogIG11dGF0ZSh2YW51c19wcmFlZ3UgPSBhcy5udW1lcmljKGRpZmZ0aW1lKHRvZGF5KCksIHN5bm5pcGFldiwgdW5pdHMgPSAiZGF5cyIpKSAvIDM2NSwKICAgICAgICAga2lpcnVzID0ga2VzdHZ1c19taW51dGl0ZXMgLyBkaXN0YW50c19rbSkgJT4lIAogICMgZmlsdGVyKGtpaXJ1cyA8IDE1LCBraWlydXMgPiAxKSAlPiUKICBncm91cF9ieShwcm9maWxlLCB2YW51c19wcmFlZ3UsIHN1Z3UpICU+JQogIHN1bW1hcmlzZShraWlydXNfbWVkaWFuID0gbWVkaWFuKGtpaXJ1cywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbiA9IG4oKSkgJT4lIAogIGZpbHRlcihuID49IDEwLCBraWlydXNfbWVkaWFuIDw9IDEwLCBraWlydXNfbWVkaWFuID49IDMpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdncGxvdChhZXModmFudXNfcHJhZWd1LCBraWlydXNfbWVkaWFuLCBjb2xvdXIgPSBzdWd1KSkgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWVfaXBzdW1fcmMoKSArCiAgbGFicyh4ID0gInZhbnVzIiwKICAgICAgIHkgPSAidGVtcG8gbWluL2ttIiwKICAgICAgIHRpdGxlID0gIktlc2ttaXNlIGpvb2tzdSB0ZW1wbyBqYSB2YW51c2Ugc2VvcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJJZ2EgcHVua3QgdMOkaGlzdGFiIMO8aGUgaW5pbWVzZSBtZWRpYWFuIHRlbXBvdCIpCmBgYAoKCk1pbGxpbmUgb24gdmFudXNlIGphIG1lZGlhYW4ga2Vza21pbmUgam9rc3UgZGlzdGFudHNpIHZhaGVsaW5lIHNlb3M/CmBgYHtyfQp0cmVubmlkX3Rvb2RlbGR1ZCAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBmaWx0ZXIoIWlzLm5hKHN5bm5pcGFldiksIAogICAgICAgICBzcG9yZGlhbGEgPT0gInJ1bm5pbmciLCAKICAgICAgICAgIWlzLm5hKGRpc3RhbnRzX2ttKSwKICAgICAgICAgIWlzLm5hKHN1Z3UpKSAlPiUgCiAgbXV0YXRlKHZhbnVzX3ByYWVndSA9IGFzLm51bWVyaWMoZGlmZnRpbWUodG9kYXkoKSwgc3lubmlwYWV2LCB1bml0cyA9ICJkYXlzIikpIC8gMzY1KSAlPiUgCiAgZ3JvdXBfYnkocHJvZmlsZSwgdmFudXNfcHJhZWd1LCBzdWd1KSAlPiUKICBzdW1tYXJpc2UoZGlzdGFuY2VfbWVkaWFuID0gbWVkaWFuKGRpc3RhbnRzX2ttLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBuID0gbigpKSAlPiUgCiAgZmlsdGVyKG4gPj0gMTAsIHZhbnVzX3ByYWVndSA+PSAxMCwgZGlzdGFuY2VfbWVkaWFuIDw9IDE1KSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBnZ3Bsb3QoYWVzKHZhbnVzX3ByYWVndSwgZGlzdGFuY2VfbWVkaWFuLCBjb2xvdXIgPSBzdWd1KSkgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWVfaXBzdW1fcmMoKSArCiAgbGFicyh4ID0gInZhbnVzIiwKICAgICAgIHkgPSAiZGlzdGFudHMsIGttIiwKICAgICAgIHRpdGxlID0gIktlc2ttaXNlIGpvb2tzdSBkaXN0YW50c2kgamEgdmFudXNlIHNlb3MiLAogICAgICAgc3VidGl0bGUgPSAiSWdhIHB1bmt0IHTDpGhpc3RhYiDDvGhlIGluaW1lc2UgbWVkaWFhbiBkaXN0YW50c2kiKQpgYGAK